package com.starmark.gateway.project.provider.service.impl;


import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.starmark.core.framework.base.AbstractBaseService;
import com.starmark.core.framework.util.RedisUtils;
import com.starmark.gateway.project.provider.dto.FilterDefinition;
import com.starmark.gateway.project.provider.dto.GatewayRouteDto;
import com.starmark.gateway.project.provider.dto.PredicateDefinition;
import com.starmark.gateway.project.provider.entity.GatewayApp;
import com.starmark.gateway.project.provider.entity.GatewayRoute;
import com.starmark.gateway.project.provider.mapper.GatewayRouteMapper;
import com.starmark.gateway.project.provider.service.IGatewayAppService;
import com.starmark.gateway.project.provider.service.IGatewayRouteService;
import com.starmark.gateway.project.provider.vo.GatewayAppMachineVo;
import com.starmark.gateway.project.provider.vo.GatewayAppVo;
import cn.hutool.core.util.StrUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author starmark
 * @since 2020-03-17
 */
@Service
public class GatewayRouteServiceImpl extends AbstractBaseService<GatewayRouteMapper, GatewayRoute> implements IGatewayRouteService {


    private static final String STARMARK_GATEWAY_ROUTES = "starmark:gateway:routes:";

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private IGatewayAppService gatewayAppService;

    @Override
    public void buildPageSearch(Map<String, Object> params, QueryWrapper<GatewayRoute> eWrapper) {
        if (params.get("keyword") != null && StrUtil.isNotEmpty(params.get("keyword") + "")) {
            eWrapper.like("name", params.get("keyword") + "");
        }

    }

    @Override
    public void deleteRouteByAppId(String appId) {
        GatewayApp gatewayApp = gatewayAppService.getById(appId);
        Assert.notNull(gatewayApp, "应用不存在");
        this.baseMapper.deleteRouteByAppId(appId);
        //设置缓存
        redisUtils.del(STARMARK_GATEWAY_ROUTES + gatewayApp.getProjectCode() + ":" + gatewayApp.getId() + "*");

    }


    /**
     * spring:
     * cloud:
     * gateway:
     * routes:
     * - id: nameRoot
     * uri: http://nameservice
     * predicates:
     * - Path=/name/**
     * - Weight=service1, 95
     * filters:
     * - StripPrefix=1
     */
    @Override
    public void createGatewayRoute(GatewayAppVo gatewayApp) {
        //创建swagger路由
        if(StrUtil.isNotEmpty(gatewayApp.getSwaggerUrl())){
            GatewayRouteDto gatewayRouteDto =this.buildSwaggerGatewayRouteDto(gatewayApp);
            this.saveGatewayRoute(gatewayApp, gatewayRouteDto);
        }
        gatewayApp.getMachineList().forEach(gatewayAppMachineVo -> {
            GatewayRouteDto gatewayRouteDto = this.buildGatewayRouteDto(gatewayApp, gatewayAppMachineVo);
            this.saveGatewayRoute(gatewayApp, gatewayRouteDto);
        });

    }

    private String getSwaggerUrl(String swaggerUrl){
        String url=swaggerUrl;
        url=url.replace("doc.html","");
        url=url.replace("swagger-ui.html","");
        return url;
    }

    private GatewayRouteDto buildSwaggerGatewayRouteDto(GatewayAppVo gatewayApp) {

        GatewayRouteDto gatewayRouteDto = new GatewayRouteDto();
        gatewayRouteDto.setId(gatewayApp.getId() + "_swagger_" + gatewayApp.getAppCode() );
        gatewayRouteDto.setFilters(createSwaggerFilterDefinition());
        gatewayRouteDto.setPredicates(createSwaggerPredicateDefinition(gatewayApp));
        gatewayRouteDto.setUri(getSwaggerUrl(gatewayApp.getSwaggerUrl()));
        gatewayRouteDto.setOrder(0);
        return gatewayRouteDto;
    }

    private List<FilterDefinition> createSwaggerFilterDefinition() {
        List<FilterDefinition> filterDefinitions=new ArrayList<>();
        {
            Map<String, String> args = new HashMap<>();
            args.put("name", "swagger_code");
            FilterDefinition filterDefinition = new FilterDefinition();
            filterDefinition.setName("RemoveRequestParameter");
            filterDefinition.setArgs(args);
            filterDefinitions.add(filterDefinition);
        }
        filterDefinitions.add(createFilterDefinition("1"));
        return filterDefinitions;

    }
    private List<PredicateDefinition> createSwaggerPredicateDefinition(GatewayAppVo gatewayAppVo) {
        List<PredicateDefinition> predicatesList = new ArrayList<>();

        //增加转发
        {
            Map<String, String> args = new HashMap<>(4);
            args.put("param", "swagger_code");
            args.put("regexp",   gatewayAppVo.getAppCode() );
                 PredicateDefinition predicateDefinition = new PredicateDefinition();
            predicateDefinition.setArgs(args);
            predicateDefinition.setName("Query");
            predicatesList.add(predicateDefinition);

        }



        return predicatesList;

    }
    @Override
    public void overload() {
        List<GatewayRoute> routes = this.list();
        routes.forEach(route -> {
            redisUtils.set(STARMARK_GATEWAY_ROUTES + route.getProjectCode() + ":" + route.getRouteId(), route.getRouteContext());

        });
    }




    private GatewayRouteDto buildGatewayRouteDto(GatewayAppVo gatewayApp, GatewayAppMachineVo gatewayAppMachineVo) {

        GatewayRouteDto gatewayRouteDto = new GatewayRouteDto();
        gatewayRouteDto.setId(gatewayApp.getId() + "_" + gatewayAppMachineVo.getServerId());
        gatewayRouteDto.setPredicates(createPredicateDefinition(gatewayApp, gatewayAppMachineVo));
        gatewayRouteDto.setFilters(Collections.singletonList(createFilterDefinition("1")));
        gatewayRouteDto.setUri(gatewayAppMachineVo.getMachineHost());
        gatewayRouteDto.setOrder(1);
        return gatewayRouteDto;
    }

    private void saveGatewayRoute(GatewayAppVo gatewayApp, GatewayRouteDto gatewayRouteDto) {
        GatewayRoute gatewayRoute = new GatewayRoute();

        gatewayRoute.setAppId(gatewayApp.getId());
        gatewayRoute.setRouteContext(JSONObject.toJSONString(gatewayRouteDto));
        gatewayRoute.setRouteId(gatewayRouteDto.getId());
        this.save(gatewayRoute);
        redisUtils.set(STARMARK_GATEWAY_ROUTES + gatewayRoute.getProjectCode() + ":" + gatewayRouteDto.getId(), gatewayRoute.getRouteContext());

    }

    private List<PredicateDefinition> createPredicateDefinition(GatewayAppVo gatewayAppVo, GatewayAppMachineVo gatewayAppMachineVo) {
        List<PredicateDefinition> predicatesList = new ArrayList<>();
        //增加读RequestBody
        // {
        // Map<String, String> args = new HashMap<>();
        // args.put("inClass",   String.class.getName());

        // PredicateDefinition predicateDefinition = new PredicateDefinition();
        ///  predicateDefinition.setArgs(args);
        // predicateDefinition.setName("ReadBodyPredicateFactory");
        // predicatesList.add(predicateDefinition);
        //}
        //增加转发
        {
            Map<String, String> args = new HashMap<>(4);
            args.put("pattern", "/" + gatewayAppVo.getProjectCode() + "/" + gatewayAppVo.getAccessPre());
            args.put("pathPattern", "/" + gatewayAppVo.getProjectCode() + "/" + gatewayAppVo.getAccessPre());
            PredicateDefinition predicateDefinition = new PredicateDefinition();
            predicateDefinition.setArgs(args);
            predicateDefinition.setName("Path");
            predicatesList.add(predicateDefinition);
        }
        //增加权重
        if (gatewayAppMachineVo.getWeight() != null) {
            Map<String, String> args = new HashMap<>();
            args.put("weight.group", gatewayAppMachineVo.getServerId());
            args.put("weight.weight", gatewayAppMachineVo.getWeight().toString());
            PredicateDefinition predicateDefinition = new PredicateDefinition();
            predicateDefinition.setArgs(args);
            predicateDefinition.setName("Weight");
            predicatesList.add(predicateDefinition);
        }

        return predicatesList;

    }

    private FilterDefinition createFilterDefinition(String parts) {
        Map<String, String> args = new HashMap<>();
        args.put("parts", parts);
        FilterDefinition filterDefinition = new FilterDefinition();
        filterDefinition.setName("StripPrefix");
        filterDefinition.setArgs(args);
        return filterDefinition;

    }
}
